#include <qqtgpiocontroler.h>

#if 0
#include <QString>

//关于PINBASE ID
//用户来设置这个BASE ID，它的增量为PIN的位宽。
//这个BASE ID其实是用户自定义的。
//#define PINBASEID 240

//PIN CACHE [就算不公开出来里边也会用]
//对所有PIN的更改都会表现在这个Cache里。
//在cache里保存了pin的详细信息，包括gpioXXX名称/base/export状态/direction/数字ID/value/listenmode
//dict["named pin 1"]["name"] = "gpioXXX名称"
//dict["named pin 1"]["ID"] = "数字ID"
//dict["named pin 1"]["base"] = "基地址"
//dict["named pin 1"]["export"] = "true"
//dict["named pin 1"]["direction"] = "in"
//dict["named pin 1"]["value"] = "0"
//dict["named pin 1"]["listenmode"] = "none"
QQtDictionary& GPIO_PINCache();

//帮助用户在pincache里初始化一个pin，简化设置过程，然后用户再自行设置需要的状态。
//初始值：输出/数字ID=0/不监听/base=0xFFFFFFFF/value=0
bool GPIO_InitPIN ( const QString& nameed_pin );

/**
  * 公开给外部的函数
 */
//内部pin使用命名pin，所有对pin的操作，输入参数为pin的ID。用户在pincache里初始化每个pin，以下函数都会使用。

//导出一个PIN，这个PIN不能是专用PIN，必须是通用PIN。
//导出一次即可保留在操作系统里，重启不会丢失。
bool GPIO_ExportPIN ( int pinID );

//停止导出一个PIN。
bool GPIO_UnexportPIN ( int pinID );

//设置/获取 PIN方向
bool GPIO_Direction ( int pinID );

//设置/获取 PIN IN时 监听模式
//默认为不监听
bool GPIO_ListenMode ( int pinID );

//每次 GPIO PIN IN Direction 数目发生改变，需要调用一次。
bool GPIO_StartListen();

//设置/读取 VALUE
bool GPIO_Value ( int pinID );

//readyRead(PIN，VALUE) 监听回调函数
typedef bool ( *GPIO_ReadyReadFunction ) ( int pinID, bool pinValue );
bool GPIO_ReadyRead ( GPIO_ReadyReadFunction* func );
#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <poll.h>

/****************************************************************
* Constants
****************************************************************/
//#define QQTTEST_GPIO
#ifdef QQTTEST_GPIO
#define SYSFS_GPIO_DIR "."
#else
#define SYSFS_GPIO_DIR "/sys/class/gpio"
#endif

#define POLL_TIMEOUT (3 * 1000) /* 3 seconds */
#define MAX_BUF 64

/****************************************************************
 * gpio_export
 ****************************************************************/
int gpio_export ( unsigned int gpio )
{
    int fd, len;
    char buf[MAX_BUF];

    fd = open ( SYSFS_GPIO_DIR "/export", O_WRONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/export %d error", gpio );
        perror ( buf );
        return fd;
    }

    len = snprintf ( buf, sizeof ( buf ), "%d", gpio );
    write ( fd, buf, len );
    close ( fd );

    return 0;
}

/****************************************************************
 * gpio_unexport
 ****************************************************************/
int gpio_unexport ( unsigned int gpio )
{
    int fd, len;
    char buf[MAX_BUF];

    fd = open ( SYSFS_GPIO_DIR "/unexport", O_WRONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/unexport %d error", gpio );
        perror ( buf );
        return fd;
    }

    len = snprintf ( buf, sizeof ( buf ), "%d", gpio );
    write ( fd, buf, len );
    close ( fd );

    return 0;
}

/****************************************************************
 * gpio_is_exported
 ****************************************************************/
int gpio_is_exported ( unsigned int gpio, bool* is_exported )
{
    int ret = 0;
    int value = 0;
    ret = gpio_get_value ( gpio, &value );

    if ( ret < 0 )
    {
        *is_exported = false;
    }
    else
    {
        *is_exported = true;
    }

    return 0;
}

/****************************************************************
 * gpio_configure_pin_direction
 ****************************************************************/
int gpio_configure_pin_direction ( unsigned int gpio, bool in )
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR  "/gpio%d/direction", gpio );

    fd = open ( buf, O_WRONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/direction %d error", gpio );
        perror ( buf );
        return fd;
    }

    if ( in )
        write ( fd, "in", 3 );
    else
        write ( fd, "out", 4 );

    close ( fd );
    return 0;
}

/****************************************************************
 * gpio_configure_pin_edge
 ****************************************************************/

int gpio_configure_pin_edge ( unsigned int gpio, const char* edge )
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/edge", gpio );

    fd = open ( buf, O_WRONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/set-edge %d error", gpio );
        perror ( buf );
        return fd;
    }

    write ( fd, edge, strlen ( edge ) + 1 );
    close ( fd );
    return 0;
}

/****************************************************************
 * gpio_get_pin_direction
 ****************************************************************/
int gpio_get_pin_direction ( unsigned int gpio, bool* in )
{
    int fd, len;
    char buf[MAX_BUF];
    char ch[4];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/direction", gpio );

    fd = open ( buf, O_RDONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/get-direction %d error", gpio );
        perror ( buf );
        return fd;
    }

    memset ( ch, '\0', 4 );
    read ( fd, ch, 3 );

    if ( memcmp ( ch, "in", 2 ) == 0 )
    {
        *in = true;
    }
    else
    {
        *in = false;
    }

    close ( fd );
    return 0;
}

/****************************************************************
 * gpio_get_pin_edge
 ****************************************************************/
int gpio_get_pin_edge ( unsigned int gpio, char* edge, unsigned int strlen )
{
    int fd, len;
    char buf[MAX_BUF];
    char ch[8];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/edge", gpio );

    fd = open ( buf, O_RDONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/get-edge %d error", gpio );
        perror ( buf );
        return fd;
    }

    memset ( ch, '\0', 8 );
    read ( fd, ch, 7 );

    strncpy ( edge, ch, strlen );

    close ( fd );
    return 0;
}

/****************************************************************
 * gpio_fd_open
 ****************************************************************/
int gpio_fd_open ( unsigned int gpio )
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/value", gpio );

    fd = open ( buf, O_RDONLY | O_NONBLOCK );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/fd_open %d error", gpio );
        perror ( buf );
    }
    return fd;
}

/****************************************************************
 * gpio_fd_close
 ****************************************************************/
int gpio_fd_close ( int fd )
{
    return close ( fd );
}

/****************************************************************
 * gpio_set_value
 ****************************************************************/
int gpio_set_value ( unsigned int gpio, int value )
{
    int fd, len;
    char buf[MAX_BUF];

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/value", gpio );

    fd = open ( buf, O_WRONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/set-value %d error", gpio );
        perror ( buf );
        return fd;
    }

    if ( value )
        write ( fd, "1", 2 );
    else
        write ( fd, "0", 2 );

    close ( fd );
    return 0;
}

/****************************************************************
 * gpio_get_value
 ****************************************************************/
int gpio_get_value ( unsigned int gpio, int* value )
{
    int fd, len;
    char buf[MAX_BUF];
    char ch;

    len = snprintf ( buf, sizeof ( buf ), SYSFS_GPIO_DIR "/gpio%d/value", gpio );

    fd = open ( buf, O_RDONLY );
    if ( fd < 0 )
    {
        snprintf ( buf, sizeof ( buf ), "gpio/get-value %d error", gpio );
        perror ( buf );
        return fd;
    }

    read ( fd, &ch, 1 );

    if ( ch != '0' )
    {
        *value = 1;
    }
    else
    {
        *value = 0;
    }

    close ( fd );
    return 0;
}



QQtDictionary QQtGPIOControler::m_pindict = QQtDictionary();

QQtGPIOControler::QQtGPIOControler ( QObject* parent ) : QObject ( parent ) {}

QQtGPIOControler::~QQtGPIOControler() {}

void QQtGPIOControler::export_pin ( unsigned int pinid )
{
    int ret = gpio_export ( pinid );
    if ( ret < 0 )
        return;

    bool in = true;
    ret = gpio_get_pin_direction ( pinid, &in );

    QByteArray edge;
    edge.resize ( 8 );
    ret = gpio_get_pin_edge ( pinid, edge.data(), 8 );

    int value = 0;
    ret = gpio_get_value ( pinid, &value );

    QString name = QString::number ( pinid );
    m_pindict[name]["id"] = name;
    m_pindict[name]["export"] = "true";
    m_pindict[name]["direction"] = in ? "in" : "out";
    m_pindict[name]["edge"] = QString ( edge );
    m_pindict[name]["value"] = value;
}

void QQtGPIOControler::unexport_pin ( unsigned int pinid )
{
    int ret = gpio_unexport ( pinid );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    m_pindict.getMap().remove ( name );
}

bool QQtGPIOControler::is_exported ( unsigned int pinid )
{
    bool b_exp = false;
    int ret = gpio_is_exported ( pinid, &b_exp );
    if ( ret < 0 )
        return false;

    return b_exp;
}

void QQtGPIOControler::configure_pin_direction ( unsigned int pinid, bool in )
{
    int ret = gpio_configure_pin_direction ( pinid, in );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    m_pindict[name]["direction"] = in ? "in" : "out";
}

void QQtGPIOControler::get_pin_direction ( unsigned int pinid, bool& in )
{
    bool in_flag = true;
    int ret = gpio_get_pin_direction ( pinid, &in_flag );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    m_pindict[name]["direction"] = in_flag ? "in" : "out";
    in = in_flag;
}

void QQtGPIOControler::set_value ( unsigned int pinid, int value )
{
    int ret = gpio_set_value ( pinid, value );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    m_pindict[name]["value"] = value;
}

int QQtGPIOControler::get_value ( unsigned int pinid )
{
    int value = 0;
    get_value ( pinid, value );
    return value;
}

QQtDictionary& QQtGPIOControler::pinDict() { return m_pindict; }

void QQtGPIOControler::get_value ( unsigned int pinid, int& value )
{
    int val = 0;
    int ret = gpio_get_value ( pinid, &val );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    m_pindict[name]["value"] = val;
    value = val;
}

QQtGPIOControlerV2::QQtGPIOControlerV2 ( QObject* parent ) : QQtGPIOControler ( parent )
{
    m_timer = new QTimer ( this );
    m_timer->setSingleShot ( false );
    m_timer->setInterval ( 100 );
    connect ( m_timer, SIGNAL ( timeout() ), this, SLOT ( slotTimeOut() ) );
}

QQtGPIOControlerV2::~QQtGPIOControlerV2() {}

QQtGPIOControlerV2* QQtGPIOControlerV2::Instance ( QObject* parent )
{
    static QQtGPIOControlerV2* inst = new QQtGPIOControlerV2 ( parent );
    return inst;
}

void QQtGPIOControlerV2::configure_pin_edge ( unsigned int pinid, int edge )
{
    QByteArray bytes;
    switch ( edge )
    {
        case EdgeNone:
            bytes = "none";
            break;
        case EdgeRising:
            bytes = "rising";
            break;
        case EdgeFalling:
            bytes = "falling";
            break;
        case EdgeBoth:
            bytes = "both";
            break;
        default:
            bytes = "none";
            break;
    }

    int ret = gpio_configure_pin_edge ( pinid, bytes.constData() );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    pinDict() [name]["edge"] = QString ( bytes );
}

void QQtGPIOControlerV2::get_pin_edge ( unsigned int pinid, int& edge )
{
    QByteArray bytes;
    bytes.resize ( 8 );
    int ret = gpio_get_pin_edge ( pinid, bytes.data(), 8 );
    if ( ret < 0 )
        return;

    QString name = QString::number ( pinid );
    pinDict() [name]["edge"] = bytes;
    if ( bytes == "none" )
        edge = EdgeNone;
    else if ( bytes == "rising" )
        edge = EdgeRising;
    else if ( bytes == "falling" )
        edge = EdgeFalling;
    else if ( bytes == "both" )
        edge = EdgeBoth;
    else
        edge = EdgeNone;
}

void QQtGPIOControlerV2::setTimerInterval ( int millsecond )
{
    m_timer->setInterval ( millsecond );
}

int QQtGPIOControlerV2::timerInterval()
{
    return m_timer->interval();
}

void QQtGPIOControlerV2::startListen()
{
    m_timer->start();
}

void QQtGPIOControlerV2::endListen()
{
    m_timer->stop();
}

void QQtGPIOControlerV2::slotTimeOut()
{
    static QQtDictionary dict1 = pinDict();

    //ok
    //qDebug() << "hahahaha";

    //如果二者数目不同，需要再更新。
    QQtDictionaryMapIterator i0 ( pinDict().getMap() );
    while ( i0.hasNext() )
    {
        i0.next();
        const QString& name = i0.key();
        const QQtDictionary& items = i0.value();
        if ( !dict1.hasChild ( name ) )
        {
            dict1[name] = pinDict() [name];
        }
    }

    //此处必须使用const的，不带const的QMap是另外一套。
    QQtDictionaryMutableMapIterator itor ( pinDict().getMap() );
    while ( itor.hasNext() )
    {
        itor.next();
        const QString& name = itor.key();
        const QQtDictionary& items = itor.value();
        unsigned int pinid = name.toUInt();

        if ( items["edge"].getValue().toString() == "none" )
        {
            //没有设置中断功能
        }
        else
        {
            //是监听的，

            int value = get_value ( pinid );
            //qDebug() << value << items["value"].getValue().toInt() << dict1[name]["value"].getValue().toInt();

            if ( items["edge"].getValue().toString() == "rising" )
            {
                //上升沿报告
                if ( dict1[name]["value"] == items["value"] )
                {
                    //中断下，数值没有发生改变。
                }
                else if ( items["value"].getValue().toInt() == 1 )
                {
                    //更新局部静态value
                    //发射信号
                    dict1[name]["value"] = items["value"];
                    emit readyRead ( pinid, dict1[name]["value"].getValue().toInt() );
                }
                else
                {
                    dict1[name]["value"] = items["value"];
                }
            }
            else if ( items["edge"].getValue().toString() == "falling" )
            {
                //下降沿报告
                if ( dict1[name]["value"] == items["value"] )
                {
                    //中断下，数值没有发生改变。
                }
                else if ( items["value"].getValue().toInt() == 0 )
                {
                    //更新局部静态value
                    //发射信号
                    dict1[name]["value"] = items["value"];
                    emit readyRead ( pinid, dict1[name]["value"].getValue().toInt() );
                }
                else
                {
                    dict1[name]["value"] = items["value"];
                }
            }
            else
            {
                //EdgeBoth 如果数值发生改变就发射信号。
                if ( dict1[name]["value"] == items["value"] )
                {
                    //中断下，数值没有发生改变。
                }
                else
                {
                    //更新局部静态value
                    //发射信号
                    dict1[name]["value"] = items["value"];
                    emit readyRead ( pinid, dict1[name]["value"].getValue().toInt() );
                }
            }
        }
    }

    //如果二者数值不同，需要再更新。
    QQtDictionaryMapIterator i1 ( pinDict().getMap() );
    while ( i1.hasNext() )
    {
        i1.next();
        const QString& name = i1.key();
        const QQtDictionary& items = i1.value();
        if ( dict1[name].getMap() != pinDict() [name].getMap() )
        {
            dict1[name] = pinDict() [name];
        }
    }
}

QDebug& operator << ( QDebug dbg, const QQtGPIO& gpio )
{
    QString name = QString::number ( gpio.pinid() );
    QQtGPIOControler instance;
    if ( instance.pinDict().getMap().contains ( name ) )
        dbg << instance.pinDict() [name];
    else
        dbg << "unexisted pinid:" << name;
    return dbg;
}

QQtGPIO::QQtGPIO ( unsigned int pinid, QObject* parent )
    : m_pinid ( pinid ), QObject ( parent )
{
    inst = QQtGPIOControlerV2::Instance ();
}

QQtGPIO::~QQtGPIO() {}

void QQtGPIO::export_pin()
{
    inst->export_pin ( m_pinid );
}

void QQtGPIO::unexport_pin()
{
    inst->unexport_pin ( m_pinid );
}

void QQtGPIO::is_exported()
{
    inst->is_exported ( m_pinid );
}

void QQtGPIO::configure_pin_direction ( bool in )
{
    inst->configure_pin_direction ( m_pinid, in );
}

void QQtGPIO::get_pin_direction ( bool& in )
{
    inst->get_pin_direction ( m_pinid, in );
}

void QQtGPIO::set_value ( int value )
{
    inst->set_value ( m_pinid, value );
}

void QQtGPIO::get_value ( int& value )
{
    inst->get_value ( m_pinid, value );
}

void QQtGPIO::configure_pin_edge ( int edge )
{
    inst->configure_pin_edge ( m_pinid, edge );
}

void QQtGPIO::get_pin_edge ( int& edge )
{
    inst->get_pin_edge ( m_pinid, edge );
}

const unsigned int QQtGPIO::pinid() const
{
    return m_pinid;
}

void QQtGPIO::set_pinid ( unsigned int pinid )
{
    m_pinid = pinid;
}
